﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TiposComuns.Objectos;
using System.Collections;
using System.Runtime.CompilerServices;

namespace Servidor.Objectos.Pesquisa
{
    /// <summary>
    /// This class keeps the k nodes closest to 
    /// a given key
    /// </summary>
    [Serializable]
    public class KClosest
    {
        #region Static
        /// <summary>
        /// Obtains the distance between two hash values, based on the xor metric
        /// </summary>
        /// <param name="k1">1st hash value</param>
        /// <param name="k2">2nd hash value</param>
        /// <returns>Distance, as a biginteger</returns>
        public static BigInteger CalculateDistance(byte []k1,byte []k2)
        {
            byte []res = new byte[k1.Length];

            for (int i = 0; i < k1.Length; i++)
            {
                res[i] = (byte)(k1[i] ^ k2[i]);
            }

            return new BigInteger(res);
        }
        #endregion

        #region Attributes

        /// <summary>
        /// Key
        /// </summary>
        public byte[] Key
        {
            get;
            set;
        }

        private SortedArray nodes;         //K closest nodes, ordered by the distance to the user
        private List<Person> recent;                         //Holds recent added nodes whith whom there wasn't any comunication
        private List<string> maps = new List<string>();

        public SortedArray Nodes
        {
            get { return nodes; }
            set { nodes = value; }
        }

        public List<Person> Recent
        {
            get { return recent; }
            set { recent = value; }
        }

        public List<string> Maps
        {
            get { return maps; }
            set { maps = value; }
        }
        #endregion

        #region Constructors
        public KClosest()
        {
        }

        public KClosest(byte[] key, SortedArray nodes)
        {
            this.Key = key;
            this.nodes = nodes;
            this.recent = new List<Person>();
            for (int i = 0; i < nodes.Lenght; i++)
            {
                this.recent.Add(nodes.Array[i]);
            }
        }

        #endregion

        #region Methods
        public void Add(object obj)
        {
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public static KClosest ToKClosest(byte []hash, KBucket buc)
        {
            SortedArray list = new SortedArray(KBucket.K);
            Person rem;
            foreach (Person per in buc.Bucs)
            {
                list.Add(KClosest.CalculateDistance(hash,per.Hash),per, out rem);
            }
            return new KClosest(hash,list);
        }
        
        /// <summary>
        /// Tries to add all the elements to this structure, preserving only the k-closest to the user's key
        /// </summary>
        /// <param name="news">K-nodes to insert</param>
        /// <returns>True, if any of the new node is inserted</returns>
        
        public bool AddAll(KClosest news)
        {
            bool flag = false;

            for (int i = 0; i < news.nodes.Lenght;i++ )
            {
                Person person = news.nodes.Array[i];
                BigInteger dist = news.nodes.Keys[i];
                Person rem;
                bool aux = nodes.Add(dist, person, out rem);
                flag = aux || flag;
                if (aux&&!maps.Contains(person.Username))
                {
                    this.recent.Add(person);
                    maps.Add(person.Username);
                }
                if (rem != null)
                {
                    this.recent.Remove(rem);
                    maps.Remove(rem.Username);
                }
            }

            return flag;
        }

        /// <summary>
        /// Return the num most recent nodes with whom there wasn't a communication
        /// </summary>
        /// <param name="num">Number of returned nodes</param>
        /// <returns>Nodes</returns>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public List<Person> GetRecent(int num)
        {
            List<Person> list = new List<Person>();
            for (int i = Math.Min(recent.Count - 1, num - 1); i >= 0; i--)
            {
                list.Add(recent[i]);
                recent.RemoveAt(i);
            }

            return list;
        }

        #endregion

    }
}
